<?php

declare(strict_types=1);

namespace App\Middleware;

use App\Core\Log;
use App\Repositories\AppsRepository;
use App\Traits\ReturnTrait;
use Phper666\JwtAuth\Exception\TokenValidException;
use Hyperf\Validation\Contract\ValidatorFactoryInterface;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Hyperf\Di\Annotation\Inject;

class ApiCheckMiddleware implements MiddlewareInterface
{
    use ReturnTrait;
    /**
     * @var ContainerInterface
     */
    protected $container;

    private $app;

    /**
     * @Inject()
     * @var ValidatorFactoryInterface
     */
    protected $validationFactory;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $params = $request->getMethod()=='GET'?$request->getQueryParams():$request->getParsedBody();
        $rules    = [
            'app_id'      => 'required',
            'sign'        => 'required',
            'timestamps' => 'required|integer',
            'nonce_str' => 'required',
        ];
        $messages = [
            'app_id.required' => 'app_id缺失',
            'sign.required'   => 'sign缺失',
            'timestamps.required' => 'timestamps缺失',
            'nonce_str.required' => 'nonceStr缺失',
        ];
        $v = $this->validationFactory->make($params, $rules, $messages);
        try {
            if ($v->fails()) {
                throw new \Exception($v->errors()->first(),100010);
            }
            if(time()-$params['timestamps']>15*60){
                throw new \Exception('时间戳不正确',100010);
            }
            $app = AppsRepository::getInstance()->one(['id','app_id','app_secret'],[[
                'field' => 'app_id',
                'value' => $params['app_id']
            ]]);
            $this->app = $app;
            if (!$app){
                throw new \Exception('应用不存在',100010);
            }
            //检查随机字符串是否已存在
            if(redis()->get($params['nonce_str'])){
                throw new \Exception('请求重复',100010);
            }
            redis()->setex($params['nonce_str'],15*60,$params['timestamps']);
            $result = $this->checkSign($params);
            if(!$result['success']){
                throw new \Exception($result['msg'],100010);
            }

        } catch (\Exception $e) {
            throw new TokenValidException($e->getMessage(), $e->getCode());
        }

        return $handler->handle($request);
    }

    protected function checkSign($params){
        $sign = array_key_exists('sign', $params) ? $params['sign'] : '';

        if (empty($sign))
            return $this->error('签名为空');

        unset($params['sign']);
        $wait_check_params= [
            'app_id' => $params['app_id'],
            'nonce_str' => $params['nonce_str'],
            'timestamps' => $params['timestamps']
        ];
        if ($sign != $this->generateSign($wait_check_params))
            return $this->error('签名认证失败');

        return $this->success([]);
    }

    protected function generateSign($params){

        $tmps = array();
        foreach ($params as $k => $v) {
            $tmps[] = $k . $v;
        }
        $string = $this->app->app_secret . implode('', $tmps);
        return strtoupper(md5($string));
    }
}